<script setup lang="ts">
import { onMounted, ref } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";

/* dom */
const container = ref<HTMLDivElement | null>(null);
/* data */
const clock = new THREE.Clock();
const mouse = ref<THREE.Vector2>(new THREE.Vector2(0.0, 0.0));
const resolution = ref<THREE.Vector2>(new THREE.Vector2(0.0, 0.0));

onMounted(() => {
    init();

    window.addEventListener("mousemove", onMouse);
});

const init = () => {
    const { clientWidth, clientHeight } = container.value as HTMLDivElement;
    resolution.value.set(clientWidth, clientHeight);
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(
        75,
        clientWidth / clientHeight,
        1,
        1000
    );
    camera.position.set(0, 0, 20);
    camera.lookAt(0, 0, 0);
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(clientWidth, clientHeight);
    container.value?.appendChild(renderer.domElement);

    const helper = new THREE.AxesHelper(10);
    scene.add(helper);

    const control = new OrbitControls(camera, renderer.domElement);

    const mesh = initBox(scene);

    computed();

    function render() {
        mesh.material.uniforms.u_time.value = clock.getElapsedTime();

        control.update();
        renderer.render(scene, camera);
        requestAnimationFrame(render);
    }
    render();
};

const initBox = (
    scene: THREE.Scene
): THREE.Mesh<THREE.PlaneGeometry, THREE.ShaderMaterial> => {
    // 顶点着色器
    const vertexShader = `
        varying vec2 v_uv;
        varying vec3 v_position;

        void main() {
            v_uv = uv;
            v_position = position;

            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
    `;
    // 片源着色器
    const fragmentShader = `
        uniform float u_time;
        uniform vec2 u_mouse;
        uniform vec2 u_resolution;

        varying vec2 v_uv;
        varying vec3 v_position;

        float rect(vec2 pt, vec2 size, vec2 center) {
            float right = center.x - size.x / 2.0;
            float left = center.x + size.x / 2.0;
            float top = center.y + size.y / 2.0;
            float bottom = center.y - size.y / 2.0;
            float xIn = step(right, pt.x) * (1.0 - step(left, pt.x));
            float yIn = step(bottom, pt.y) * (1.0 - step(top, pt.y));

            return xIn * yIn;
        }

        void main() {
            // gl_FragColor = vec4(u_mouse.x / u_resolution.x, u_mouse.y / u_resolution.y, abs(sin(u_time)), 1.0);
            // gl_FragColor = vec4(abs(sin(u_time)), 0.0, abs(cos(u_time)), 1.0);
            // gl_FragColor = vec4(mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), gl_FragCoord.y / u_resolution.y), 1.0);
            // gl_FragColor = vec4(v_uv.x, 0.0, v_uv.y, 1.0);
            // vec3 color = mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), v_uv.x)
            // gl_FragColor = vec4(mix(vec3(1.0, 0.0, 0.0), vec3(0.0, 0.0, 1.0), v_uv.x), 1.0);

            // clamp 方法使用
            // vec3 color = clamp(v_position, vec3(0.0), vec3(1.0));
            // gl_FragColor = vec4(abs(color.x), abs(color.y), 0.0, 1.0);

            // step 方法使用
            // vec3 color = vec3(0.0);
            // color.r = step(0.0, v_position.x);
            // color.g = step(0.0, v_position.y);
            // gl_FragColor = vec4(color, 1.0);

            // smoothstep 方法使用
            // vec3 color = vec3(0.0);
            // color.r = smoothstep(-2.5, 2.5, v_position.x);
            // color.g = smoothstep(-2.5, 2.5, v_position.y);
            // gl_FragColor = vec4(color, 1.0);

            // 用所学的知识画一个圆
            // float inCircle = 1.0 - step(5.0, length(v_position.xy));
            // vec3 color = vec3(1.0, 1.0, 0.0) * inCircle;

            // 用所学的知识画一个正方形
            vec3 color = vec3(1.0, 0.0, 0.0) * rect(v_position.xy, vec2(1.0), vec2(-1.0, 0.0)) + vec3(0.0, 1.0, 0.0) * rect(v_position.xy, vec2(2.0), vec2(2.0, 0.0));

            gl_FragColor = vec4(color, 1.0);
        }
    `;
    // 让正方体按照给定半径的圆形轨迹进行移动
    const fragmentShader1 = `
        uniform float u_time;
        varying vec3 v_position;
        varying vec2 v_uv;

        float rect(vec2 pt, vec2 size, vec2 center) {
            vec2 p = pt - center; // 根据中心点 计算出位移后的顶点坐标
            vec2 s = size * 0.5; // 计算正方形边缘
            float w = step(-s.x, p.x) - step(s.x, p.x); // 是否在x范围内
            float h = step(-s.y, p.y) - step(s.y, p.y); // 是否在y范围内

            return w * h;
        }

        mat2 rotate(float angle) {
            float x = cos(angle); // 0.7
            float y = sin(angle); // 0.7
            return mat2(
                x, -y,
                y, x
            );
        }

        mat2 scaling(vec2 scale) {
            return mat2(
                1.0 * (1.0 / scale.x), 0.0,
                0.0, 1.0 * (1.0 / scale.y)
            );
        }

        void main() {
            float radius = 3.0;
            vec2 scale = vec2(2.0, 2.0);
            float x = cos(u_time) * radius;
            float y = sin(u_time) * radius;
            float tileCount = 5.0;

            vec2 center = vec2(0.5);

            mat2 r = rotate(u_time);

            float sv = (1.0 - sin(u_time)) * 0.5 + 1.0;
            mat2 s = scaling(vec2(sv));

            vec2 p = fract(v_uv * tileCount);

            // vec2 pt = (r * s * (v_position.xy - center)) + center;
            vec2 pt = r * (p - center) + center;

            vec3 color = vec3(1.0, 1.0, 0.0) * rect(pt, vec2(0.5), center);

            gl_FragColor = vec4(color, 1.0);
        }
    `;
    // 画圆
    const fragmentShader2 = `
        varying vec3 v_position;

        // float circle(vec2 pt, vec2 center, float radius, bool soften) {
        //     float edge = (soften) ? radius * 0.05 : 0.0;
        //     return 1.0 - smoothstep(radius - edge, radius + edge, length(pt - center));
        // }

        float circle(vec2 pt, vec2 center, float radius, float width) {
            float l = length(pt - center);
            float edge = width * 0.5;
            return step(radius - edge, l) - step(radius + edge, l);
        }

        void main() {
            vec2 center = vec2(1.0);

            float c = circle(v_position.xy, center, 1.0, 0.1);

            vec3 color = vec3(1.0, 1.0, 0.0) * c;

            gl_FragColor = vec4(color, 1.0);
        }
    `;
    // 画线
    const fragmentShader3 = `
        varying vec3 v_position;
        varying vec2 v_uv;
        uniform float u_time;
        const float PI = 3.1415926;
        const float PI2 = 3.1415926 * 2.0;

        float line(float a, float b, float width, float smoothWidth) {
            float edge = width * 0.5;
            return smoothstep(a - edge - smoothWidth, a - edge, b) - smoothstep(a + edge, a + edge + smoothWidth, b);
        }

        float circle(vec2 pt, vec2 center, float radius, float width) {
            float l = length(pt - center); 
            float edge = width * 0.5;
            return step(radius - edge, l) - step(radius + edge, l);
        }

        float sweep(vec2 pt, vec2 center, float radius, float width, float edge) {
            vec2 d = pt - center; // 根据中心点计算点位置
            float theta = fract(u_time / 4.0) * PI2; // 角度
            vec2 p = vec2(cos(theta), -sin(theta)) * radius; // 圆边缘坐标
            float h = clamp(dot(d, p) / dot(p, p), 0.0, 1.0); // 
            float l = length(d - p * h);

            // 遮罩层
            float gradient = 0.0;
            const float gradient_angle = PI * 0.5;
            if (length(d) < radius) {
                float angle = mod(theta + atan(d.y, d.x), PI2);
                gradient = clamp(gradient_angle - angle, 0.0, gradient_angle) / gradient_angle * 0.5;
            }

            return gradient + 1.0 - smoothstep(width, width + edge, l);
        }

        float yuan(vec2 pt, vec2 center, float radius, bool is_turn) {
            vec2 p = pt - center;
            if (is_turn) {
                p.x -= cos(u_time * 2.0) * 0.08;
            } else {
                p.x += cos(u_time * 2.0) * 0.08;
            }
            float c = 1.0 - step(0.0, length(p) - radius);

            return c;
        }

        void main() {
            vec3 axis_color = vec3(0.8);
            vec3 color = axis_color * line(v_uv.y, 0.5, 0.002, 0.001);
            color += axis_color * line(v_uv.x, 0.5, 0.002, 0.001);
            color += axis_color * circle(v_uv, vec2(0.5), 0.45, 0.002);
            color += axis_color * circle(v_uv, vec2(0.5), 0.3, 0.002);
            color += axis_color * circle(v_uv, vec2(0.5), 0.15, 0.002);
            color += vec3(0.1, 0.3, 1.0) * sweep(v_uv, vec2(0.5), 0.45, 0.002, 0.001);
            color += axis_color * yuan(v_uv, vec2(0.75, 0.5), 0.01, false);
            color += axis_color * yuan(v_uv, vec2(0.25, 0.5), 0.01, true);

            gl_FragColor = vec4(color, 1.0);
        }
    `;
    // 砖块
    const fragmentShader4 = `
        uniform float u_time;
        varying vec3 v_position;
        varying vec2 v_uv;

        float line(vec2 pt, vec2 center, float width) {
            vec2 p = pt - center;

            float y = 1.0 - step(0.0, mod(p.y, 0.2));

            return y;
        }

        void main() {
            vec3 color = vec3(0.1);
            vec3 my_color = vec3(1.0);

            color += my_color * line(v_uv, vec2(0.5), 0.02);
        
            gl_FragColor = vec4(color, 1.0);
        }
    `;
    // 噪音
    const fragmentShader5 = /* glsl */ `
        uniform float u_time;
        varying vec3 v_position;
        varying vec2 v_uv;

        float random(vec2 st, float speed) {
            const float a = 453.7295790;
            const float b = 273.5764891;
            const float c = 3214.56754389;

            return fract(sin(dot(st, vec2(a, b)) + speed) * c);
        }

        void main() {
            vec3 color = vec3(1.0) * random(v_uv, u_time);

            gl_FragColor = vec4(color, 1.0);
        }
    `;

    const material = new THREE.ShaderMaterial({
        uniforms: {
            u_time: { value: clock.getElapsedTime() },
            u_mouse: { value: mouse.value },
            u_resolution: { value: resolution.value },
        },
        vertexShader,
        fragmentShader: fragmentShader5,
        side: THREE.DoubleSide,
        transparent: true,
    });

    const geo = new THREE.PlaneGeometry(10, 10);

    const mesh = new THREE.Mesh(geo, material);

    // mesh.position.set(0, 0, 5);

    scene.add(mesh);

    return mesh;
};

const onMouse = (event: MouseEvent) => {
    const { x, y } = event;
    mouse.value.set(x, resolution.value.y - y);
};

const computed = () => {};
</script>

<template>
    <div ref="container" class="container"></div>
</template>

<style lang="less" scoped>
.container {
    width: 100%;
    height: 100%;
}
</style>
